package com.zyj.mvcframework.v1;

import com.zyj.mvcframework.annoation.ZyjAutowired;
import com.zyj.mvcframework.annoation.ZyjController;
import com.zyj.mvcframework.annoation.ZyjRequestMapping;
import com.zyj.mvcframework.annoation.ZyjService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class ZyjDisPatcherServlet extends HttpServlet {
    private List<String> classNames = new ArrayList<>();
    private Map<String, Object> mapping = new HashMap<>();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        try {
            doDisPather(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException{
        //获取application.properties属性
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
        //读取scanPackage属性
        Properties properties = new Properties();
        try {
            properties.load(is);
            String scanPackage = properties.getProperty("scanPackage");
            doScan(scanPackage);
            for(String className : classNames){
                if(!className.contains(".")){
                    continue;
                }
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(ZyjController.class)) {
                    String baseUrl = "";
                    mapping.put(className, clazz.newInstance());
                    if(clazz.isAnnotationPresent(ZyjRequestMapping.class)){
                        ZyjRequestMapping zyjRequestMapping = clazz.getAnnotation(ZyjRequestMapping.class);
                        baseUrl = zyjRequestMapping.value();
                    }
                    Method[] methods = clazz.getDeclaredMethods();
                    for(Method method : methods){
                        ZyjRequestMapping zyjRequestMapping = method.getAnnotation(ZyjRequestMapping.class);
                        String url = baseUrl + zyjRequestMapping.value();
                        mapping.put(url, method);
                    }
                } else if(clazz.isAnnotationPresent(ZyjService.class)) {
                    ZyjService zyjService = clazz.getAnnotation(ZyjService.class);
                    String beanName = zyjService.value();
                    if ("".equals(beanName)) {
                        beanName = clazz.getName();
                    }
                    Object obj = clazz.newInstance();
                    mapping.put(beanName, obj);
                    for (Class<?> i : clazz.getInterfaces()) {
                        mapping.put(i.getName(), obj);
                    }
                }else{
                    continue;
                }
            }
            for(Object obj : mapping.values()){
                if(obj == null){continue;}
                Class clazz = obj.getClass();
                if(clazz.isAnnotationPresent(ZyjController.class)){
                    Field[] fields = clazz.getDeclaredFields();
                    for(Field field : fields){
                        if(field.isAnnotationPresent(ZyjAutowired.class)){
                            ZyjAutowired zyjAutowired  = field.getAnnotation(ZyjAutowired.class);
                            String beanName = zyjAutowired.value();
                            if("".equals(beanName)){
                                beanName = field.getType().getName();
                            }
                            field.setAccessible(true);
                            try {
                                field.set(mapping.get(clazz.getName()), mapping.get(beanName));
                            } catch (IllegalArgumentException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(is != null) {
                    is.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(mapping);
        System.out.println("mvc init succussfully");
    }

    private void doDisPather(HttpServletRequest req ,HttpServletResponse resp) throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        if(!mapping.containsKey(url)){
            resp.getWriter().write("500");
            return;
        }
        Map<String, String[]> map = req.getParameterMap();
        Method method = (Method) mapping.get(url);
        method.invoke(mapping.get(method.getDeclaringClass().getName()), new Object[]{req, resp, map.get("name")[0]});
    }
    /**
     * 读取scanpackage目录下的所有文件，并且返回文件名
     *  return demoService -> com.xx.xxx.demoService
     */
    private void doScan(String scanPackage){
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
        File classDir = new File(url.getFile());
        for(File dir : classDir.listFiles()){
            if(dir.isDirectory()){
                doScan(scanPackage + "." + dir.getName());
            }
            if(dir.getName().endsWith(".class")){
                String fileName = scanPackage + "." + dir.getName().replace(".class", "");
                classNames.add(fileName);
            }
        }
    }
}
